{/* Copyright 2020 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License. */}

import {Layout} from '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-aria/select';
import collectionsDocs from 'docs:@react-types/shared/src/collections.d.ts';
import selectionDocs from 'docs:@react-stately/selection';
import statelyDocs from 'docs:@react-stately/select';
import overlaysDocs from 'docs:@react-aria/overlays';
import focusDocs from 'docs:@react-aria/focus';
import listboxDocs from 'docs:@react-aria/listbox';
import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-aria/select/package.json';
import Anatomy from './anatomy.svg';
import ChevronRight from '@spectrum-icons/workflow/ChevronRight';
import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard';
import examplePreview from 'url:./example.png';
import styledExamplePreview from 'url:./styled-components.png';
import popupExamplePreview from 'url:./popup-example.png';

---
category: Pickers
keywords: [listbox, select, aria]
---

# useSelect

<PageDescription>{docs.exports.useSelect.description}</PageDescription>

<HeaderInfo
  packageData={packageData}
  componentNames={['useSelect']}
  sourceData={[
    {type: 'W3C', url: 'https://www.w3.org/WAI/ARIA/apg/patterns/listbox/'}
  ]} />

## API

<FunctionAPI function={docs.exports.useSelect} links={docs.links} />

## Features

A select can be built using the [&lt;select&gt;](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
and [&lt;option&gt;](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option) HTML elements, but this is
not possible to style consistently cross browser, especially the options. `useSelect` helps achieve accessible
select components that can be styled as needed without compromising on high quality interactions.

* Exposed to assistive technology as a button with a `listbox` popup using ARIA (combined with [useListBox](../ListBox/useListBox.html))
* Support for selecting a single option
* Support for disabled options
* Support for sections
* Labeling support for accessibility
* Support for native HTML constraint validation with customizable UI, custom validation functions, realtime validation, and server-side validation errors
* Support for mouse, touch, and keyboard interactions
* Tab stop focus management
* Keyboard support for opening the listbox using the arrow keys, including automatically focusing
  the first or last item accordingly
* Typeahead to allow selecting options by typing text, even without opening the listbox
* Browser autofill integration via a hidden native `<select>` element
* Mobile screen reader listbox dismissal support

## Anatomy

<Anatomy />

A select consists of a label, a button which displays a selected value, and a listbox, displayed in a
popup. Users can click, touch, or use the keyboard on the button to open the listbox popup. `useSelect`
handles exposing the correct ARIA attributes for accessibility and handles the interactions for the
select in its collapsed state. It should be combined with [useListBox](../ListBox/useListBox.html), which handles
the implementation of the popup listbox.

`useSelect` also supports optional description and error message elements, which can be used
to provide more context about the field, and any validation messages. These are linked with the
input via the `aria-describedby` attribute.

`useSelect` returns props that you should spread onto the appropriate element:

<TypeContext.Provider value={docs.links}>
  <InterfaceType properties={docs.links[docs.exports.useSelect.return.base?.id ?? docs.exports.useSelect.return.id].properties} />
</TypeContext.Provider>

State is managed by the <TypeLink links={statelyDocs.links} type={statelyDocs.exports.useSelectState} />
hook from `@react-stately/select`. The state object should be passed as an option to `useSelect`

If a select does not have a visible label, an `aria-label` or `aria-labelledby`
prop must be passed instead to identify it to assistive technology.

## State management

`useSelect` requires knowledge of the options in the select in order to handle keyboard
navigation and other interactions. It does this using the <TypeLink links={collectionsDocs.links} type={collectionsDocs.exports.Collection} />
interface, which is a generic interface to access sequential unique keyed data. You can
implement this interface yourself, e.g. by using a prop to pass a list of item objects,
but <TypeLink links={statelyDocs.links} type={statelyDocs.exports.useSelectState} /> from
`@react-stately/select` implements a JSX based interface for building collections instead.
See [Collection Components](v3:collections.html) for more information.

In addition, <TypeLink links={statelyDocs.links} type={statelyDocs.exports.useSelectState} />
manages the state necessary for multiple selection and exposes
a <TypeLink links={selectionDocs.links} type={selectionDocs.exports.SelectionManager} />,
which makes use of the collection to provide an interface to update the selection state.
It also holds state to track if the popup is open.
For more information about selection, see [Selection](v3:selection.html).

## Example

This example uses a `<button>` element for the trigger, with a `<span>` inside to hold the value,
and another for the dropdown arrow icon (hidden from screen readers with `aria-hidden`).
A &lt;<TypeLink links={docs.links} type={docs.exports.HiddenSelect} />&gt; is used to render a hidden native
`<select>`, which enables browser form autofill support.

The same `Popover`, `ListBox`, and `Button` components created with [usePopover](../Popover/usePopover.html), [useListBox](../ListBox/useListBox.html),
and [useButton](../Button/useButton.html) that you may already have in your component library or application should be reused. These can be shared with other
components such as a `ComboBox` created with [useComboBox](../ComboBox/useComboBox.html) or a `Dialog` popover created with [useDialog](../Modal/useDialog.html).
The code for these components is also included below in the collapsed sections.

In addition, see [useListBox](../ListBox/useListBox.html) for examples of sections (option groups), and more complex
options. For an example of the description and error message elements, see [useTextField](../TextField/useTextField.html).

```tsx example export=true
import {HiddenSelect, useSelect} from '@react-aria/select';
import {Item} from '@react-stately/collections';
import {useSelectState} from '@react-stately/select';

// Reuse the ListBox, Popover, and Button from your component library. See below for details.
import {ListBox, Popover, Button} from 'your-component-library';

function Select(props) {
  // Create state based on the incoming props
  let state = useSelectState(props);

  // Get props for child elements from useSelect
  let ref = React.useRef(null);
  let {
    labelProps,
    triggerProps,
    valueProps,
    menuProps,
    hiddenSelectProps
  } = useSelect(props, state, ref);

  return (
    <div style={{display: 'inline-block'}}>
      <div {...labelProps}>{props.label}</div>
      <HiddenSelect {...hiddenSelectProps} />
      <Button
        {...triggerProps}
        buttonRef={ref}
        style={{height: 30, fontSize: 14}}>
        <span {...valueProps}>
          {state.selectedItem
            ? state.selectedItem.rendered
            : 'Select an option'
          }
        </span>
        <span
          aria-hidden="true"
          style={{paddingLeft: 5}}>
          ▼
        </span>
      </Button>
      {state.isOpen &&
        <Popover state={state} triggerRef={ref} placement="bottom start">
          <ListBox
            {...menuProps}
            state={state} />
        </Popover>
      }
    </div>
  );
}

<Select label="Favorite Color">
  <Item>Red</Item>
  <Item>Orange</Item>
  <Item>Yellow</Item>
  <Item>Green</Item>
  <Item>Blue</Item>
  <Item>Purple</Item>
  <Item>Black</Item>
  <Item>White</Item>
  <Item>Lime</Item>
  <Item>Fushsia</Item>
</Select>
```

### Popover

The `Popover` component is used to contain the popup listbox for the Select.
It can be shared between many other components, including [ComboBox](../ComboBox/useComboBox.html),
[Menu](../Menu/useMenu.html), and others.
See [usePopover](../Popover/usePopover.html) for more examples of popovers.

<details>
  <summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show code</summary>

```tsx example export=true render=false
import type {AriaPopoverProps} from 'react-aria';
import type {OverlayTriggerState} from 'react-stately';
import {usePopover, Overlay, DismissButton} from '@react-aria/overlays';

interface PopoverProps extends Omit<AriaPopoverProps, 'popoverRef'> {
  children: React.ReactNode,
  state: OverlayTriggerState
}

function Popover({children, state, ...props}: PopoverProps) {
  let popoverRef = React.useRef(null);
  let {popoverProps, underlayProps} = usePopover({
    ...props,
    popoverRef
  }, state);

  return (
    <Overlay>
      <div {...underlayProps} style={{position: 'fixed', inset: 0}} />
      <div
        {...popoverProps}
        ref={popoverRef}
        style={{
          ...popoverProps.style,
          background: 'var(--page-background)',
          border: '1px solid gray'
        }}>
        <DismissButton onDismiss={state.close} />
        {children}
        <DismissButton onDismiss={state.close} />
      </div>
    </Overlay>
  );
}
```

</details>

### ListBox

The `ListBox` and `Option` components are used to show the list of options.
They can also be shared with other components like a [ComboBox](../ComboBox/useComboBox.html). See
[useListBox](../ListBox/useListBox.html) for more examples, including sections and more complex items.

<details>
  <summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show code</summary>

```tsx example export=true render=false
import {useListBox, useOption} from '@react-aria/listbox';

function ListBox(props) {
  let ref = React.useRef(null);
  let {listBoxRef = ref, state} = props;
  let {listBoxProps} = useListBox(props, state, listBoxRef);

  return (
    <ul
      {...listBoxProps}
      ref={listBoxRef}
      style={{
        margin: 0,
        padding: 0,
        listStyle: "none",
        maxHeight: 150,
        overflow: "auto",
        minWidth: 100,
        background: 'lightgray'
      }}>
      {[...state.collection].map(item => (
        <Option
          key={item.key}
          item={item}
          state={state} />
      ))}
    </ul>
  );
}

function Option({item, state}) {
  let ref = React.useRef(null);
  let {optionProps, isSelected, isFocused, isDisabled} = useOption({key: item.key}, state, ref);

  return (
    <li
      {...optionProps}
      ref={ref}
      style={{
        background: isFocused ? 'gray' : 'transparent',
        color: isDisabled ? 'gray' : isFocused ? 'white' : 'black',
        padding: '2px 5px',
        outline: 'none',
        cursor: 'pointer',
        display: 'flex',
        justifyContent: 'space-between',
        gap: '10px'
      }}>
      {item.rendered}
      {isSelected ? <span>✓</span> : null}
    </li>
  );
}
```

</details>

### Button

The `Button` component is used in the above example to toggle the listbox popup. It is built using the [useButton](../Button/useButton.html) hook, and can be shared with many other components.

<details>
  <summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show code</summary>

```tsx example export=true render=false
import {useButton} from '@react-aria/button';

function Button(props) {
  let ref = props.buttonRef;
  let {buttonProps} = useButton(props, ref);
  return <button {...buttonProps} ref={ref} style={props.style}>{props.children}</button>;
}
```

</details>

## Styled examples

<ExampleCard
  url="https://codesandbox.io/s/hardcore-moon-xzc4r?file=/src/ComboBox.tsx"
  preview={examplePreview}
  title="Tailwind CSS"
  description="An example of styling a Select with Tailwind." />

<ExampleCard
  url="https://codesandbox.io/s/sharp-sun-e3fgd?file=/src/Select.tsx"
  preview={styledExamplePreview}
  title="Styled Components"
  description="A Select with complex item content built with Styled Components." />

<ExampleCard
  url="https://codesandbox.io/s/heuristic-margulis-uz3d4d?file=/src/Select.tsx"
  preview={popupExamplePreview}
  title="Popup positioning"
  description="A Select with custom macOS-style popup positioning." />

## Usage

The following examples show how to use the Select component created in the above example.

### Dynamic collections

`Select` follows the [Collection Components API](v3:collections.html), accepting both static and dynamic collections.
The examples above show static collections, which can be used when the full list of options is known ahead of time. Dynamic collections,
as shown below, can be used when the options come from an external data source such as an API call, or update over time.

As seen below, an iterable list of options is passed to the Select using the `items` prop. Each item accepts a `key` prop, which
is passed to the `onSelectionChange` handler to identify the selected item. Alternatively, if the item objects contain an `id` property,
as shown in the example below, then this is used automatically and a `key` prop is not required.

```tsx example
function Example() {
  let options = [
    {id: 1, name: 'Aerospace'},
    {id: 2, name: 'Mechanical'},
    {id: 3, name: 'Civil'},
    {id: 4, name: 'Biomedical'},
    {id: 5, name: 'Nuclear'},
    {id: 6, name: 'Industrial'},
    {id: 7, name: 'Chemical'},
    {id: 8, name: 'Agricultural'},
    {id: 9, name: 'Electrical'}
  ];

  return (
    <>
      <Select label="Pick an engineering major" items={options}>
        {(item) => <Item>{item.name}</Item>}
      </Select>
    </>
  );
}
```

### Controlled selection

Setting a selected option can be done by using the `defaultSelectedKey` or `selectedKey` prop. The selected key corresponds to the `key` of an item.
When `Select` is used with a dynamic collection as described above, the key of each item is derived from the data.
See the `react-stately` [Selection docs](v3:selection.html) for more details.

```tsx example
function Example() {
  let options = [
    {name: 'Koala'},
    {name: 'Kangaroo'},
    {name: 'Platypus'},
    {name: 'Bald Eagle'},
    {name: 'Bison'},
    {name: 'Skunk'}
  ];
  let [animal, setAnimal] = React.useState("Bison");

  return (
    <Select
      label="Pick an animal (controlled)"
      items={options}
      selectedKey={animal}
      onSelectionChange={selected => setAnimal(selected)}>
      {item => <Item key={item.name}>{item.name}</Item>}
    </Select>
  );
}
```

### Asynchronous loading

This example uses the [useAsyncList](../useAsyncList.html) hook to handle asynchronous loading
of data from a server. You may additionally want to display a spinner to indicate the loading
state to the user, or support features like infinite scroll to load more data.

```tsx example
import {useAsyncList} from '@react-stately/data';

function AsyncLoadingExample() {
  let list = useAsyncList({
    async load({signal, filterText}) {
      let res = await fetch(
        `https://pokeapi.co/api/v2/pokemon`,
        {signal}
      );
      let json = await res.json();

      return {
        items: json.results
      };
    }
  });

  return (
    <Select label="Pick a Pokemon" items={list.items} selectionMode="single">
      {(item) => <Item key={item.name}>{item.name}</Item>}
    </Select>
  );
}
```

### Disabled

A Select can be fully disabled using the `isDisabled` prop.

```tsx example
<Select label="Choose frequency" isDisabled>
  <Item key="rarely">Rarely</Item>
  <Item key="sometimes">Sometimes</Item>
  <Item key="always">Always</Item>
</Select>
```

### Disabled options

`useSelect` supports marking items as disabled using the `disabledKeys` prop. Each key in this list
corresponds with the `key` prop passed to the `Item` component, or automatically derived from the values passed
to the `items` prop. See [Collections](v3:collections.html) for more details.

Disabled items are not focusable, selectable, or keyboard navigable. The `isDisabled` property returned by
`useOption` can be used to style the item appropriately.

```tsx example
<Select label="Favorite Animal" disabledKeys={['cat', 'kangaroo']}>
  <Item key="red panda">Red Panda</Item>
  <Item key="cat">Cat</Item>
  <Item key="dog">Dog</Item>
  <Item key="aardvark">Aardvark</Item>
  <Item key="kangaroo">Kangaroo</Item>
  <Item key="snake">Snake</Item>
</Select>
```

### Controlled open state

The open state of the select can be controlled via the `defaultOpen` and `isOpen` props

```tsx example
function Example() {
  let [open, setOpen] = React.useState(false);

  return (
    <>
      <p>Select is {open ? 'open' : 'closed'}</p>
      <Select label="Choose frequency" isOpen={open} onOpenChange={setOpen}>
        <Item key="rarely">Rarely</Item>
        <Item key="sometimes">Sometimes</Item>
        <Item key="always">Always</Item>
      </Select>
    </>
  );
}
```

### Links

By default, interacting with an item in a Select triggers `onSelectionChange`. Alternatively, items may be links to another page or website. This can be achieved by passing the `href` prop to the `<Item>` component. Link items in a `Select` are not selectable. See the [links](../ListBox/useListBox.html#links) section in the `useListBox` docs for details on how to support this.

## Internationalization

`useSelect` and `useListBox` handle some aspects of internationalization automatically.
For example, type to select is implemented with an
[Intl.Collator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator)
for internationalized string matching. You are responsible for localizing all labels and option
content that is passed into the select.

### RTL

In right-to-left languages, the select should be mirrored. The arrow should be on the left,
and the selected value should be on the right. In addition, the content of list options should
flip. Ensure that your CSS accounts for this.
